package com.clockey.server.httpserver.netty;

import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpHeaders.Names;
import io.netty.handler.codec.http.HttpHeaders.Values;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http.QueryStringDecoder;

import java.net.URI;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.clockey.server.httpserver.cache.ControllerMethodCache;
import com.clockey.server.httpserver.netty.bean.Modle;
import com.clockey.server.httpserver.netty.mvc.method.ControllerMethod;
import com.google.common.base.Charsets;

/**
 * HttpServer Handler, refer to http://kanpiaoxue.iteye.com/blog/2163332
 * 
 * @author littleBirdTao
 *
 */
// @Sharable //note that this Handler is not sharable
public class HttpServerHandler extends ChannelInboundHandlerAdapter {

	private static final Logger logger = LoggerFactory.getLogger(HttpServerHandler.class);

	private HttpRequest nettyHttpRequest;
	/** Get request flag **/
	@SuppressWarnings("unused")
	private boolean requestGetFl;
	/** Post request flag **/
	private boolean requestPostFl;
	private String uriPath;
	private String contentType;
	private ByteBufToBytes reader;

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		if (msg instanceof HttpRequest) {
			nettyHttpRequest = (HttpRequest) msg;
			HttpMethod method = nettyHttpRequest.getMethod();
			requestGetFl = method.equals(HttpMethod.GET);
			requestPostFl = method.equals(HttpMethod.POST);

			if (HttpHeaders.is100ContinueExpected(nettyHttpRequest)) {
				FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE);
				ctx.writeAndFlush(response);
			}
			String uriStr = nettyHttpRequest.getUri();
			uriPath = new URI(uriStr).getPath();
			contentType = nettyHttpRequest.headers().get(HttpHeaders.Names.CONTENT_TYPE);
			
			// if Post request
			if (requestPostFl) {
				int contentLength = (int) HttpHeaders.getContentLength(nettyHttpRequest);
				reader = new ByteBufToBytes(contentLength);
			} else {
				QueryStringDecoder decoderQuery = new QueryStringDecoder(uriStr);
				Request request = new Request();
				request.setQueryStringDecoder(decoderQuery);
				handleController(ctx, request);
				return;
			}
		}
		// if Post request
		if (requestPostFl) {
			if (msg instanceof HttpContent) {
				HttpContent content = (HttpContent) msg;
				reader.reading(content.content());
				if (reader.isEnd() || msg instanceof LastHttpContent) {
					byte[] contentBytes = reader.readFull();
					Request request = new Request();
					String contentString = new String(contentBytes, Charsets.UTF_8);
					request.setQueryString(contentString);
					//request.setQueryStringDecoder(new QueryStringDecoder("some?" + contentString));//如果需要post方式获得parameter,取消注释
					handleController(ctx, request);
				}
			}
		}
	}

	private void handleController(ChannelHandlerContext ctx, Request request) {
		ControllerMethod controllerMethod = ControllerMethodCache.getControllerMethodCache().get(uriPath);// /clockey/user/login;
		Modle modle = new Modle();
		if (controllerMethod == null) {
			modle.setHttpResponseStatus(HttpResponseStatus.NOT_FOUND);
			modle.setContent("404 Not Found");
		} else {
			if(StringUtils.containsIgnoreCase(contentType, Modle.CONTENT_TYPE_JSON)){//如果上传是application/json类型必须上传map
				if(request.getParameterMap() != null){//上传是map
					try {
						controllerMethod.execute(request, modle.getContentMap());
					} catch (Exception e) {
						logger.error("excep: ", e);
						modle.setHttpResponseStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR);
						modle.setContent("500 Internal Server Error");
					}
				}else{//上传
					modle.setHttpResponseStatus(HttpResponseStatus.BAD_REQUEST);
					modle.setContent("Bad Request");
				}
			}else{//如果不是application/json类型
				try {
					controllerMethod.execute(request, modle);
				} catch (Exception e) {
					logger.error("excep: ", e);
					modle.setHttpResponseStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR);
					modle.setContent("500 Internal Server Error");
				}
			}
		}
		writeHttpResponse(ctx, modle);
	}

	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		ctx.flush();
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
		logger.error("excep: ", cause);
		ctx.close();
	}

	private void writeHttpResponse(ChannelHandlerContext ctx, Modle modle) {
		FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, modle.getHttpResponseStatus(), modle.getContent());
		response.headers().set(Names.CONTENT_TYPE, modle.getContentType());
		response.headers().set(Names.CONTENT_LENGTH, response.content().readableBytes());
		response.headers().set(Names.EXPIRES, 0);
		if (HttpHeaders.isKeepAlive(nettyHttpRequest)) {
			response.headers().set(Names.CONNECTION, Values.KEEP_ALIVE);
			ctx.writeAndFlush(response);
		} else {
			ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
		}
	}

}